// Copyright 2019 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

package org.citra.citra_emu;

import android.content.Context;
import android.text.TextUtils;

import com.aiwu.InputManager;
import com.aiwu.Util;
import com.aiwu.library.AiwuSimulator;
import com.aiwu.library.App;
import com.aiwu.library.abs.AbsCheatListener;
import com.aiwu.library.bean.BurstOperateButtonBean;
import com.aiwu.library.bean.CheatAddResult;
import com.aiwu.library.bean.CheatChildBean;
import com.aiwu.library.bean.CheatDataBean;
import com.aiwu.library.bean.CheatGroupBean;
import com.aiwu.library.bean.ClickOperateButtonBean;
import com.aiwu.library.bean.Menu;
import com.aiwu.library.bean.OperateConfig;
import com.aiwu.library.bean.RockerOperateButtonBean;
import com.aiwu.library.util.DisplayUtil;
import com.aiwu.library.util.IOUtil;
import com.aiwu.library.util.LogUtil;

import org.citra.citra_emu.activities.EmulationActivity;
import org.citra.citra_emu.model.GameDatabase;
import org.citra.citra_emu.utils.DirectoryInitialization;
import org.citra.citra_emu.utils.PermissionsHandler;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CitraApplication extends App {
    private static final String TAG = CitraApplication.class.getName();
    public static GameDatabase databaseHelper;
    private static CitraApplication application;

    //region修改
    private static String romName = "";
    //endregion

    @Override
    public void onCreate() {
        super.onCreate();
        application = this;

        if (PermissionsHandler.hasWriteAccess(getApplicationContext())) {
            DirectoryInitialization.start(getApplicationContext());
        }

        databaseHelper = new GameDatabase(this);
        initSimulator();
    }

    public static Context getAppContext() {
        return application.getApplicationContext();
    }

    private void initSimulator() {
        AiwuSimulator.setDebug(BuildConfig.DEBUG);
        AiwuSimulator.setDefaultOperateConfig(false);
        OperateConfig.Builder builder = getBuildWithLandSet();
        setPortSet(builder);
        AiwuSimulator.setSpeedMultipleArray(new int[]{1, 2, 4});
        AiwuSimulator.init(builder.build(), Menu.FAST_CHEAT, Menu.FAST_SPEED, Menu.FAST_SNAP, Menu.FAST_MEMORY, Menu.FAST_ARCHIVE, Menu.FAST_ORIENTATION, Menu.FAST_SETTING, Menu.BOTTOM_EXIT, Menu.BOTTOM_RELOAD, Menu.BOTTOM_SETTING, Menu.BOTTOM_OPERATE, Menu.BOTTOM_SOUND, Menu.BOTTOM_HANDLE, Menu.BOTTOM_FIGHT);

        AiwuSimulator.setCheatListener(new AbsCheatListener() {
            @Override
            public String getCheatFilePath(CheatDataBean cheatDataBean) {
                String romName = cheatDataBean.getRomName();
                if (TextUtils.isEmpty(romName)) {
                    romName = getRomName();
                }
                if (TextUtils.isEmpty(romName)) {
                    romName = "错误的金手指";
                }
                return Util.getCheatDir() + "/" + romName + ".txt";
            }

            @Override
            public void write2CheatFile(String cheatFilePath, CheatDataBean cheatDataBean) {
                StringBuilder cheatSb = new StringBuilder();
                for (CheatGroupBean cheat : cheatDataBean.getCheatList()) {
                    List<CheatChildBean> children = cheat.getChildren();
                    if (children == null) {
                        cheatSb.append("[")
                                .append(cheat.getDesc())
                                .append("]")
                                .append("\n");
                        String cheatCode = cheat.getCheatCode();
                        String[] cheatCodeArray = cheatCode.split(",");
                        for (String code : cheatCodeArray) {
                            code = code.replace("-", " ");
                            cheatSb.append(code)
                                    .append("\n");
                        }
                    } else {
                        for (CheatChildBean bean : children) {
                            cheatSb.append("[")
                                    .append(cheat.getDesc())
                                    .append(":")
                                    .append(bean.getDesc())
                                    .append("]")
                                    .append("\n");
                            String cheatCode = bean.getCheatCode();
                            String[] cheatCodeArray = cheatCode.split(",");
                            for (String code : cheatCodeArray) {
                                code = code.replace("-", " ");
                                cheatSb.append(code)
                                        .append("\n");
                            }
                        }
                    }
                }
                int index = cheatSb.lastIndexOf("\n");
                if (index != -1) {
                    cheatSb.replace(index, cheatSb.length(), "");
                }
                IOUtil.writeFileFromString(cheatFilePath, cheatSb.toString());
            }

            @Override
            public String getNetCheatCodeRegex() {
                return "[A-Z0-9]{8}-[A-Z0-9]{8}";
            }

            @Override
            public String getCheatCodeHint() {
                return "输入作弊码内容,格式：xxxxxxxx xxxxxxxx\n多条换行显示例如：\n1208B334 0000027C\n2208B324 00000003";
            }

            @Override
            public void setCheatEnabled(int groupPosition, int childPosition, int convertedPosition, int numberOfInstructions, boolean enabled, boolean isGroup) {
                NativeLibrary.SetCheatEnabled(convertedPosition, enabled);
            }

            @Override
            public CheatAddResult addCheat(String desc, String code, boolean user) {
                String[] lines = code.toUpperCase().trim().split("\\n");
                Pattern p = Pattern.compile(" *([A-Z0-9]{8}) +([A-Z0-9]{1,8}) *");
                StringBuilder codeSb = new StringBuilder();
                for (String line : lines) {
                    line = line.replace("\r", "");
                    Matcher m = p.matcher(line);
                    boolean match = m.matches();
                    if (!match) {
                        return new CheatAddResult(false);
                    }
                    String address = m.group(1) == null ? "" : m.group(1);
                    String value = m.group(2) == null ? "" : m.group(2);
                    while (value.length() < 8) {
                        value = "0" + value;
                    }
                    codeSb.append(address)
                            .append(" ")
                            .append(value)
                            .append("\n");
                }
                int index = codeSb.lastIndexOf("\n");
                if (index != -1) {
                    codeSb.replace(index, codeSb.length(), "");
                }
                String codeStr = codeSb.toString();
                boolean result = NativeLibrary.AddCheat(desc, codeStr, false);
                if (!result) {
                    return new CheatAddResult(false);
                }
                LogUtil.d(TAG, "添加金手指：" + desc + "---" + codeStr);
                return new CheatAddResult(true, desc, codeStr.replace(" ", "-"), "\n");
            }

            @Override
            public boolean deleteCheat(int groupPosition, int convertedPosition, int numberOfInstructions) {
                NativeLibrary.DeleteCheat(convertedPosition);
                return true;
            }

            @Override
            public boolean enableEditCheatCode() {
                return true;
            }

            @Override
            public String restoreCheatCode(String code) {
                String[] lines = code.toUpperCase().trim().split(",");
                Pattern p = Pattern.compile("([A-Z0-9]{8})-([A-Z0-9]{8})");
                StringBuilder codeSb = new StringBuilder();
                for (String line : lines) {
                    Matcher m = p.matcher(line);
                    boolean match = m.matches();
                    if (!match) {
                        return code;
                    }
                    String address = m.group(1) == null ? "" : m.group(1);
                    String value = m.group(2) == null ? "" : m.group(2);
                    codeSb.append(address)
                            .append(" ")
                            .append(value)
                            .append("\n");
                }
                int index = codeSb.lastIndexOf("\n");
                if (index != -1) {
                    codeSb.replace(index, codeSb.length(), "");
                }
                return codeSb.toString();
            }

            @Override
            public CheatAddResult editCheat(String desc, String code, int groupPosition, int convertedPosition, CheatGroupBean cheatGroupBean) {
                String[] lines = code.toUpperCase().trim().split("\\n");
                Pattern p = Pattern.compile(" *([A-Z0-9]{8}) +([A-Z0-9]{1,8}) *");
                StringBuilder codeSb = new StringBuilder();
                for (String line : lines) {
                    line = line.replace("\r", "");
                    Matcher m = p.matcher(line);
                    boolean match = m.matches();
                    if (!match) {
                        return new CheatAddResult(false);
                    }
                    String address = m.group(1) == null ? "" : m.group(1);
                    String value = m.group(2) == null ? "" : m.group(2);
                    while (value.length() < 8) {
                        value = "0" + value;
                    }
                    codeSb.append(address)
                            .append(" ")
                            .append(value)
                            .append("\n");
                }
                int index = codeSb.lastIndexOf("\n");
                if (index != -1) {
                    codeSb.replace(index, codeSb.length(), "");
                }
                String codeStr = codeSb.toString();
                boolean result = NativeLibrary.UpdateCheat(convertedPosition, desc, codeStr);
                if (!result) {
                    return new CheatAddResult(false);
                }
                LogUtil.d(TAG, "修改金手指：" + desc + "---" + codeStr);
                return new CheatAddResult(true, desc, codeStr.replace(" ", "-"), "\n");
            }
        });
    }

    /**
     * 横屏的默认按钮
     */
    private OperateConfig.Builder getBuildWithLandSet() {
        //设计宽度
        int designWidth = DisplayUtil.getAppLandscapeScreenWidth();
        //设计高度
        int designHeight = DisplayUtil.getAppLandscapeScreenHeight();
        //间隔
        int spacing = DisplayUtil.dp2px(8);
        //圆形按钮宽高
        int roundBtnSize = DisplayUtil.dp2px(BurstOperateButtonBean.DEFAULT_SIZE_DP);
        //方形按钮宽
        int recBtnWidth = DisplayUtil.dp2px(ClickOperateButtonBean.DEFAULT_WIDTH_DP);
        //方形按钮高
        int recBtnHeight = DisplayUtil.dp2px(ClickOperateButtonBean.DEFAULT_HEIGHT_DP);

        //开始按钮宽
        int startBtnWidthDp = 36;
        //开始按钮高
        int startBtnHeightDp = 20;
        //开始按钮宽
        int startBtnWidth = DisplayUtil.dp2px(startBtnWidthDp);
        //开始按钮高
        int startBtnHeight = DisplayUtil.dp2px(startBtnHeightDp);

        int rButtonX = designWidth - recBtnWidth - spacing;
        int rButtonY = spacing * 2;
        int zlButtonY = rButtonY + spacing + recBtnHeight;
        int selectButtonX = (int) (designWidth / 2 - startBtnWidth * 1.3f);
        int startButtonX = selectButtonX + startBtnWidth + spacing * 2;
        int selectButtonY = designHeight - startBtnHeight - spacing * 4;
        int aButtonX = designWidth - roundBtnSize - spacing;
        int bButtonX = (int) (aButtonX - roundBtnSize * 0.9);
        int yButtonX = (int) (bButtonX - roundBtnSize * 0.9);
        int bButtonY = designHeight - roundBtnSize - spacing;
        int aButtonY = (int) (bButtonY - roundBtnSize * 0.9);
        int xButtonY = (int) (aButtonY - roundBtnSize * 0.9);

        LinkedHashSet<ClickOperateButtonBean> set = new LinkedHashSet<>();
        BurstOperateButtonBean l = new BurstOperateButtonBean(InputManager.KEY_L, spacing, rButtonY, R.drawable.button_l, R.drawable.button_l_press, false);
        l.setDefaultWidthDp(ClickOperateButtonBean.DEFAULT_WIDTH_DP);
        l.setDefaultHeightDp(ClickOperateButtonBean.DEFAULT_HEIGHT_DP);
        set.add(l);
        BurstOperateButtonBean r = new BurstOperateButtonBean(InputManager.KEY_R, rButtonX, rButtonY, R.drawable.button_r, R.drawable.button_r_press, false);
        r.setDefaultWidthDp(ClickOperateButtonBean.DEFAULT_WIDTH_DP);
        r.setDefaultHeightDp(ClickOperateButtonBean.DEFAULT_HEIGHT_DP);
        set.add(r);
        BurstOperateButtonBean zl = new BurstOperateButtonBean(InputManager.KEY_ZL, spacing, zlButtonY, R.drawable.button_zl, R.drawable.button_zl_press, false);
        zl.setDefaultWidthDp(ClickOperateButtonBean.DEFAULT_WIDTH_DP);
        zl.setDefaultHeightDp(ClickOperateButtonBean.DEFAULT_HEIGHT_DP);
        zl.setShow(false);
        set.add(zl);
        BurstOperateButtonBean zr = new BurstOperateButtonBean(InputManager.KEY_ZR, rButtonX, zlButtonY, R.drawable.button_zr, R.drawable.button_zr_press, false);
        zr.setDefaultWidthDp(ClickOperateButtonBean.DEFAULT_WIDTH_DP);
        zr.setDefaultHeightDp(ClickOperateButtonBean.DEFAULT_HEIGHT_DP);
        zr.setShow(false);
        set.add(zr);
        ClickOperateButtonBean select = new ClickOperateButtonBean(InputManager.KEY_SELECT, selectButtonX, selectButtonY, R.drawable.button_select, R.drawable.button_select_press);
        select.setDefaultWidthDp(startBtnWidthDp);
        select.setDefaultHeightDp(startBtnHeightDp);
        set.add(select);
        ClickOperateButtonBean start = new ClickOperateButtonBean(InputManager.KEY_START, startButtonX, selectButtonY, R.drawable.button_start, R.drawable.button_start_press);
        start.setDefaultWidthDp(startBtnWidthDp);
        start.setDefaultHeightDp(startBtnHeightDp);
        set.add(start);
        BurstOperateButtonBean a = new BurstOperateButtonBean(InputManager.KEY_A, aButtonX, aButtonY, R.drawable.button_a, R.drawable.button_a_press, false);
        set.add(a);
        BurstOperateButtonBean b = new BurstOperateButtonBean(InputManager.KEY_B, bButtonX, bButtonY, R.drawable.button_b, R.drawable.button_b_press, false);
        set.add(b);
        BurstOperateButtonBean x = new BurstOperateButtonBean(InputManager.KEY_X, bButtonX, xButtonY, R.drawable.button_x, R.drawable.button_x_press, false);
        set.add(x);
        BurstOperateButtonBean y = new BurstOperateButtonBean(InputManager.KEY_Y, yButtonX, aButtonY, R.drawable.button_y, R.drawable.button_y_press, false);
        set.add(y);

        //region横屏摇杆
        //主摇杆大小
        int mainRockerSizeDp = RockerOperateButtonBean.DEFAULT_SIZE_DP;
        int mainRockerSize = DisplayUtil.dp2px(mainRockerSizeDp);
        //十字键大小
        int crossRockerSizeDp = (int) (RockerOperateButtonBean.DEFAULT_SIZE_DP * 0.8f);
        int crossRockerSize = DisplayUtil.dp2px(crossRockerSizeDp);
        //C摇杆大小
        int cRockerSizeDp = (int) (RockerOperateButtonBean.DEFAULT_SIZE_DP * 0.6f);
        int cRockerSize = DisplayUtil.dp2px(cRockerSizeDp);

        int mainRockerY = designHeight - mainRockerSize - spacing;
        int crossRockerY = mainRockerY - crossRockerSize + spacing * 3;
        int cRockerX = yButtonX - spacing - cRockerSize;
        int cRockerY = designHeight - cRockerSize - spacing * 3;
        LinkedHashSet<RockerOperateButtonBean> landscapeRockers = new LinkedHashSet<>();
        RockerOperateButtonBean crossRocker = new RockerOperateButtonBean(InputManager.KEY_ROCKER_CROSS, spacing, crossRockerY, crossRockerSizeDp, crossRockerSizeDp, RockerOperateButtonBean.CROSS);
        landscapeRockers.add(crossRocker);
        RockerOperateButtonBean mainRocker = new RockerOperateButtonBean(InputManager.KEY_ROCKER_MAIN, crossRockerSize, mainRockerY, mainRockerSizeDp, mainRockerSizeDp, RockerOperateButtonBean.CLASSIC);
        landscapeRockers.add(mainRocker);
        RockerOperateButtonBean cRocker = new RockerOperateButtonBean(InputManager.KEY_ROCKER_C, cRockerX, cRockerY, cRockerSizeDp, cRockerSizeDp, RockerOperateButtonBean.CLASSIC, 0.6f);
        cRocker.setShow(false);
        landscapeRockers.add(cRocker);
        //endregion

        return new OperateConfig.Builder(set, RockerOperateButtonBean.NULL)
                .setLandscapeRockers(landscapeRockers)
                .setHandleMap(InputManager.getHandleMapValues(), InputManager.getHandleMapNames(), InputManager.getDefaultHandleMap())
                .setSupportPlayerNum(1);
    }

    /**
     * 竖屏的默认按钮
     *
     * @param builder
     */
    private void setPortSet(OperateConfig.Builder builder) {
        //设计宽度
        int designWidth = DisplayUtil.getAppLandscapeScreenHeight();
        //设计高度
        int designHeight = DisplayUtil.getAppLandscapeScreenWidth();
        //间隔
        int spacing = DisplayUtil.dp2px(6);
        //圆形按钮宽高
        int roundBtnSize = DisplayUtil.dp2px(BurstOperateButtonBean.DEFAULT_SIZE_DP);
        //方形按钮宽
        int recBtnWidth = DisplayUtil.dp2px(ClickOperateButtonBean.DEFAULT_WIDTH_DP);
        //方形按钮高
        int recBtnHeight = DisplayUtil.dp2px(ClickOperateButtonBean.DEFAULT_HEIGHT_DP);

        //开始按钮宽
        int startBtnWidthDp = 36;
        //开始按钮高
        int startBtnHeightDp = 20;
        //开始按钮宽
        int startBtnWidth = DisplayUtil.dp2px(startBtnWidthDp);
        //开始按钮高
        int startBtnHeight = DisplayUtil.dp2px(startBtnHeightDp);


        int rButtonX = designWidth - recBtnWidth - spacing;
        int rButtonY = spacing * 2;
        int zlButtonY = rButtonY + spacing + recBtnHeight;
        int selectButtonX = (int) (designWidth / 2 - startBtnWidth);
        int startButtonX = selectButtonX + startBtnWidth + spacing * 2;
        int aButtonX = designWidth - roundBtnSize - spacing;
        int bButtonX = (int) (aButtonX - roundBtnSize * 0.9);
        int yButtonX = (int) (bButtonX - roundBtnSize * 0.9);
        int bButtonY = designHeight - roundBtnSize - spacing * 3;
        int aButtonY = (int) (bButtonY - roundBtnSize * 0.9);
        int xButtonY = (int) (aButtonY - roundBtnSize * 0.9);
        int selectButtonY = xButtonY - startBtnHeight - spacing * 2;

        LinkedHashSet<ClickOperateButtonBean> set = new LinkedHashSet<>();
        BurstOperateButtonBean l = new BurstOperateButtonBean(InputManager.KEY_L, spacing, rButtonY, R.drawable.button_l, R.drawable.button_l_press, false);
        l.setDefaultWidthDp(ClickOperateButtonBean.DEFAULT_WIDTH_DP);
        l.setDefaultHeightDp(ClickOperateButtonBean.DEFAULT_HEIGHT_DP);
        set.add(l);
        BurstOperateButtonBean r = new BurstOperateButtonBean(InputManager.KEY_R, rButtonX, rButtonY, R.drawable.button_r, R.drawable.button_r_press, false);
        r.setDefaultWidthDp(ClickOperateButtonBean.DEFAULT_WIDTH_DP);
        r.setDefaultHeightDp(ClickOperateButtonBean.DEFAULT_HEIGHT_DP);
        set.add(r);
        BurstOperateButtonBean zl = new BurstOperateButtonBean(InputManager.KEY_ZL, spacing, zlButtonY, R.drawable.button_zl, R.drawable.button_zl_press, false);
        zl.setDefaultWidthDp(ClickOperateButtonBean.DEFAULT_WIDTH_DP);
        zl.setDefaultHeightDp(ClickOperateButtonBean.DEFAULT_HEIGHT_DP);
        zl.setShow(false);
        set.add(zl);
        BurstOperateButtonBean zr = new BurstOperateButtonBean(InputManager.KEY_ZR, rButtonX, zlButtonY, R.drawable.button_zr, R.drawable.button_zr_press, false);
        zr.setDefaultWidthDp(ClickOperateButtonBean.DEFAULT_WIDTH_DP);
        zr.setDefaultHeightDp(ClickOperateButtonBean.DEFAULT_HEIGHT_DP);
        zr.setShow(false);
        set.add(zr);
        ClickOperateButtonBean select = new ClickOperateButtonBean(InputManager.KEY_SELECT, selectButtonX, selectButtonY, R.drawable.button_select, R.drawable.button_select_press);
        select.setDefaultWidthDp(startBtnWidthDp);
        select.setDefaultHeightDp(startBtnHeightDp);
        set.add(select);
        ClickOperateButtonBean start = new ClickOperateButtonBean(InputManager.KEY_START, startButtonX, selectButtonY, R.drawable.button_start, R.drawable.button_start_press);
        start.setDefaultWidthDp(startBtnWidthDp);
        start.setDefaultHeightDp(startBtnHeightDp);
        set.add(start);
        BurstOperateButtonBean a = new BurstOperateButtonBean(InputManager.KEY_A, aButtonX, aButtonY, R.drawable.button_a, R.drawable.button_a_press, false);
        set.add(a);
        BurstOperateButtonBean b = new BurstOperateButtonBean(InputManager.KEY_B, bButtonX, bButtonY, R.drawable.button_b, R.drawable.button_b_press, false);
        set.add(b);
        BurstOperateButtonBean x = new BurstOperateButtonBean(InputManager.KEY_X, bButtonX, xButtonY, R.drawable.button_x, R.drawable.button_x_press, false);
        set.add(x);
        BurstOperateButtonBean y = new BurstOperateButtonBean(InputManager.KEY_Y, yButtonX, aButtonY, R.drawable.button_y, R.drawable.button_y_press, false);
        set.add(y);

        //region横屏摇杆
        //主摇杆大小
        int mainRockerSizeDp = RockerOperateButtonBean.DEFAULT_SIZE_DP;
        int mainRockerSize = DisplayUtil.dp2px(mainRockerSizeDp);
        //十字键大小
        int crossRockerSizeDp = (int) (RockerOperateButtonBean.DEFAULT_SIZE_DP * 0.8f);
        int crossRockerSize = DisplayUtil.dp2px(crossRockerSizeDp);
        //C摇杆大小
        int cRockerSizeDp = (int) (RockerOperateButtonBean.DEFAULT_SIZE_DP * 0.6f);
        int cRockerSize = DisplayUtil.dp2px(cRockerSizeDp);

        int mainRockerX = spacing * 3;
        int mainRockerY = designHeight - mainRockerSize - spacing * 3;
        int crossRockerY = mainRockerY - crossRockerSize - spacing * 4;
        int cRockerX = designWidth - cRockerSize - spacing;
        LinkedHashSet<RockerOperateButtonBean> portraitRockers = new LinkedHashSet<>();
        RockerOperateButtonBean crossRocker = new RockerOperateButtonBean(InputManager.KEY_ROCKER_CROSS, spacing, crossRockerY, crossRockerSizeDp, crossRockerSizeDp, RockerOperateButtonBean.CROSS);
        portraitRockers.add(crossRocker);
        RockerOperateButtonBean mainRocker = new RockerOperateButtonBean(InputManager.KEY_ROCKER_MAIN, mainRockerX, mainRockerY, mainRockerSizeDp, mainRockerSizeDp, RockerOperateButtonBean.CLASSIC);
        portraitRockers.add(mainRocker);
        RockerOperateButtonBean cRocker = new RockerOperateButtonBean(InputManager.KEY_ROCKER_C, cRockerX, crossRockerY, cRockerSizeDp, cRockerSizeDp, RockerOperateButtonBean.CLASSIC, 0.6f);
        cRocker.setShow(false);
        portraitRockers.add(cRocker);
        //endregion

        builder.setPortraitButtons(set)
                .setPortraitRockers(portraitRockers);
    }

    public static String getRomName() {
        if (TextUtils.isEmpty(romName)) {
            final EmulationActivity emulationActivity = NativeLibrary.sEmulationActivity.get();
            if (emulationActivity != null) {
                romName = Util.getIdFromPath(emulationActivity.getPath());
            }
        }
        return romName;
    }

    public static void setRomName(String romName) {
        CitraApplication.romName = romName;
    }
}
